//-------------------------------------------------------------------------------------------------
// Copyright (c) Bradford W. Mott and Flare Contributors
// North Carolina State University, Department of Computer Science
// The IntelliMedia Group
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//-------------------------------------------------------------------------------------------------

using System.Collections.Generic;
using UnityEngine;

using Flare.Display;
using Flare.Geom;
using Flare.Sys;

namespace Flare.Text
{
    /// <summary>
    /// The DeviceFont class represents a "device" font, which is a Unity font that's been
    /// mapped in a font catalog.
    /// </summary>
    public class DeviceFont : Flare.Text.Font
    {
        private static Dictionary<UnityEngine.Font, uint> fontTextureRevisions =
            new Dictionary<UnityEngine.Font, uint>();

        /// <summary>
        /// Return the Unity font associated with the device font.
        /// </summary>
        public UnityEngine.Font unityFont { get;
            /* \cond */ private set; /* \endcond */ }

        internal FontMetrics metrics
        {
            get
            {
                if (m_metrics == null)
                {
                    // See if there's an embedded font with the same name
                    EmbeddedFont font = ApplicationDomain.currentDomain.GetEmbeddedFont(
                        this.fullFontName);
                    if ((font != null) && (font.hasLayout))
                    {
                        m_metrics = new FontMetrics(font.ascent, font.descent, font.leading);
                    }
                }

                return m_metrics;
            }
        }
        private FontMetrics m_metrics = null;

        /// <summary>
        /// Return the revision number associated with the corresponding Unity font. Each
        /// time the Unity font texture is rebuilt the revision number will be incremented.
        /// </summary>
        public uint unityFontTextureRevision
        {
            get { return DeviceFont.fontTextureRevisions[this.unityFont]; }
        }

        /// <summary>
        /// CharacterInfo.vert.yMin is offset based on the default size of the Unity font
        /// selected in the import settings. This property returns the amount of this
        /// offset so that it can be accounted for during rendering.
        /// </summary>
        internal float vertexOffset
        {
            get
            {
#if UNITY_5
                // Looks like this offset is no longer required in Unity 5
                return 0.0f;
            }
        }
#else
                if (this.offset.HasValue)
                {
                    return this.offset.Value;
                }
                else
                {
                    // TODO bwmott 2013-08-17: What's the correct way to calculate this offset
                    // and why is it even there? For now, it looks like grabbing the yMin value
                    // for the space character gives the correct offset.
                    this.unityFont.RequestCharactersInTexture(" ");
                    CharacterInfo ci;
                    if (this.unityFont.GetCharacterInfo(' ', out ci))
                    {
                        this.offset = ci.vert.yMin;

                        return this.offset.Value;
                    }
                    else
                    {
                        return -1;
                    }
                }
            }
        }

        private float? offset = null;
#endif

        internal DeviceFont(string fontName, string fontStyle, UnityEngine.Font unityFont)
            : base(fontName, fontStyle)
        {
            this.unityFont = unityFont;

            // Set initial revision of the Unity font if it's not already being tracked
            // and add a rebuild callback
            if (!DeviceFont.fontTextureRevisions.ContainsKey(this.unityFont))
            {
                DeviceFont.fontTextureRevisions[this.unityFont] = 0;

                // TODO bwmott 2013-08-17: We should use a weak refernece here so that
                // the DeviceFont isn't kept alive due to the callback. Since there are
                // few DeviceFonts, we'll leave it this way for now...
#if UNITY_5
                UnityEngine.Font.textureRebuilt += OnFontTextureRebuilt;
#else
                this.unityFont.textureRebuildCallback += OnFontTextureRebuilt;
#endif
            }
        }

#if UNITY_5
        public void OnFontTextureRebuilt(UnityEngine.Font changedFont)
        {

            if (changedFont != this.unityFont)
            {
                return;
            }
#else
        public void OnFontTextureRebuilt()
        {
#endif
            // Unity font texture has been rebuilt so increment its revision
            DeviceFont.fontTextureRevisions[this.unityFont] += 1;
        }

        public override string ToString()
        {
            return string.Format("[DeviceFont: fontName={0} fontStyle={1} unityFont={2}]",
                fontName, fontStyle, unityFont.name);
        }
    }
}